//
//	IOBus Protocol - Object Directory
//
//  (c) 2008 mocom software GmbH & Co KG
//
//  $Author: volker $
//  $Date: 2009-09-29 13:26:19 +0200 (Di, 29. Sep 2009) $
//  $Revision: 503 $
//
//  MOCOM CONFIDENTIAL
//
#include <iobus.h>

#define MAX_WIRE_OBJECTS	32

void IOBusObjectDirCreate( IOBusObjectDir *dir )
{
	if ( dir )
	{
		IOBusHalMemorySet( dir, 0, sizeof( IOBusObjectDir ) );
	}
}

void IOBusEMStop( IOBusObjectDir *dir )
{
	for ( int idx = 0;idx < dir->entries;idx++ )
	{
		if ( dir->objects[idx].write )
		{
			dir->objects[idx].outvalue = dir->objects[idx].defvalue;
			dir->objects[idx].write( idx, dir->objects[idx].arg, dir->objects[idx].outvalue );
		}
	}
}

int IOBusObjectDirAdd( IOBusObjectDir *dir,
                       int type,
                       int inout,
					   int exp, 
                       int phy,
                       IOBusObjectValue *defvalue,
                       OBJDIR_WRITE_CALLBACK write,
                       void *arg )
{
	int idx;
	IOBusObject *object;

	if ( dir->entries >= IOBUS_MAX_OBJECTS || dir->objects[dir->entries].id != 0 )
		return -1;

	if (( type & OBJ_TYPE_MASK )  == 0 )
		return -1;

	if ( phy > 0xffff )
		return -1;

	if (exp < -127 || exp > 127)
		return -1;
	
	inout &= ( OBJ_IS_INPUT | OBJ_IS_OUTPUT );
	if ( inout == 0 )
		return -1;

	idx = dir->entries++;
	object = &dir->objects[idx];
	object->id = type | inout | phy | ((exp << OBJ_EXP_SHIFT) & OBJ_EXP_MASK);
	object->tag = 0;
	object->write = write;
	object->arg = arg;
	object->defvalue = *defvalue;
	object->outvalue = *defvalue;
	object->invalue.ivalue = 0;
	if ( write )
	{
		write( idx, object->arg, object->outvalue );
	}

	return idx;
}

void IOBusObjectChangeUnits( IOBusObjectDir *dir, unsigned idx, unsigned phyunit, int exp )
{
	if ( idx < dir->entries )
	{
		dir->objects[idx].id = ( dir->objects[idx].id & ~(OBJ_UNIT_MASK | OBJ_EXP_MASK)) | phyunit | ((exp << OBJ_EXP_SHIFT) & OBJ_EXP_MASK);
	}
}

unsigned IOBusObjectSerialize( IOBusObjectDir *dir, IOBusObjectWireImage *wire, unsigned tag, unsigned *error, unsigned long classes )
{
	unsigned idx = 0;

	wire->valid = 0;
	*error = IO_ERR_Ok;

	classes &= OBJ_CLASS_MASK;

	for ( int i = 0;i < dir->entries;i++ )
	{
		if ( (dir->objects[i].id & classes) || dir->objects[i].updated)
		{
			wire->values[idx++] = dir->objects[i].outvalue;
			if (dir->objects[i].updated)
				dir->objects[i].updated--;
			wire->valid |= ( 1 << i );
			if ( dir->objects[i].tag != tag && (( dir->objects[i].id & OBJ_CLASS_MASK ) == OBJ_IS_INPUT ) )
			{
				// non fatal
				dir->objects[i].error |= IO_ERR_Slip1;
			}
			else
			{
				// ok, it works this time, clear error
				dir->objects[i].error = IO_ERR_Ok;
			}
			// or-in error
			*error |= dir->objects[i].error;
		}
	}

	return ( idx*sizeof( IOBusObjectValue ) ) + sizeof( wire->valid );
}

void IOBusObjectDeSerialize( IOBusObjectDir *dir, IOBusObjectWireImage *wire, unsigned tag )
{
	unsigned idx, i;

	for ( i = 0, idx = 0;i < dir->entries;i++ )
	{
		if (( 1 << i ) & wire->valid )
		{
			// set value
			dir->objects[i].invalue = wire->values[idx++];
			// update tag
			dir->objects[i].tag = tag;
			// if output, call callback
			if ( dir->objects[i].write )
			{
				dir->objects[i].write( i, dir->objects[i].arg, dir->objects[i].invalue );
			}
		}
	}
}

int IOBusObjectSerializeInfo( IOBusObjectDir *dir, IOBusObjectWireImage *wire )
{
	wire->valid = ( 1 << dir->entries ) - 1;

	for ( int i = 0;i < dir->entries;i++ )
	{
		wire->values[i].ivalue = dir->objects[i].id;
	}
	return ( dir->entries *sizeof( IOBusObjectValue ) ) + sizeof( wire->valid );
}

void IOBusObjectDeSerializeInfo( IOBusObjectDir *dir, IOBusObjectWireImage *wire, unsigned tag )
{
	unsigned idx = 0;

	for ( int i = 0;i < IOBUS_MAX_OBJECTS;i++ )
	{
		if (( 1 << i ) & wire->valid )
		{
			// id
			dir->objects[i].id = wire->values[idx++].ivalue;
			// clear error
			dir->objects[i].error = IO_ERR_Ok;
			// update tag
			dir->objects[i].tag = tag;
		}
	}
	dir->entries = idx;
}

int IOBusObjectSerializeError( IOBusObjectDir *dir, IOBusObjectWireImage *wire )
{
	wire->valid = ( 1 << dir->entries ) - 1;

	for ( int i = 0;i < dir->entries;i++ )
	{
		wire->values[i].ivalue = dir->objects[i].error;
		dir->objects[i].error = IO_ERR_Ok;
	}
	return ( dir->entries *sizeof( IOBusObjectValue ) ) + sizeof( wire->valid );
}

void IOBusObjectDeSerializeError( IOBusObjectDir *dir, IOBusObjectWireImage *wire )
{
	unsigned idx = 0;

	for ( int i = 0;i < dir->entries;i++ )
	{
		if (( 1 << i ) & wire->valid )
		{
			// id
			dir->objects[i].error = wire->values[idx++].ivalue;
		}
	}
}

int IOBusObjectWriteData( IOBusObjectDir *dir, unsigned idx, IOBusObjectValue *value, unsigned tag )
{
	if ( idx < dir->entries )
	{
		if ( value )
			dir->objects[idx].outvalue = *value;
		else
			dir->objects[idx].outvalue = dir->objects[idx].defvalue;
			
		// bugfix for now, with HAGSPIO/SLAVE SOFTWARE update this will no longer be needed	
		if (( dir->objects[idx].id & OBJ_CLASS_MASK ) == OBJ_IS_OUTPUT )	
		{
			dir->objects[idx].invalue = dir->objects[idx].outvalue;
		}
		dir->objects[idx].updated=1;
		dir->objects[idx].tag = tag;
		return TRUE;
	}
	return FALSE;
}

int IOBusObjectReadData( IOBusObjectDir *dir, unsigned idx, IOBusObjectValue *value, unsigned *tag )
{
	if ( idx < dir->entries )
	{
		*value = dir->objects[idx].invalue;
		*tag = dir->objects[idx].tag;
		return TRUE;
	}
	return FALSE;
}

int IOBusObjectError( IOBusObjectDir *dir, unsigned idx, UINT32 errorcode )
{
	if ( idx < dir->entries )
	{
		dir->objects[idx].error = errorcode;
		return TRUE;
	}
	return FALSE;
}
